package com.xiashitech.agent.dns;

import com.xiashitech.agent.config.StartConfig;
import org.xbill.DNS.*;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.Objects;
import java.util.Random;

public class CustomDns {
    private static final CustomDns customNameResolver = new CustomDns();

    public static CustomDns getInstance() {
        return customNameResolver;
    }
    private Resolver resolver;
    private CustomDns() {
        String nameserver = StartConfig.getParamValueFromKey(StartConfig.nameServer); // Config 中取
        if(null == nameserver || "".equals(nameserver))
            return;
        try {
            System.out.println("CustomDns constructed, nameserver: " + nameserver);
            resolver = new SimpleResolver(nameserver);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public InetAddress[] getDnsRealIps(String hostname) {
        System.out.println("getDnsRealIp called, hostname: " + hostname + ", resolver: " + resolver);
        if(null != resolver) {
            // 创建解析器，通过构造函数指定 DNS 服务器地址
            try {
                // 定义要解析的域名，以及解析类型（这里指解析 A 记录）
                Lookup lookup = new Lookup(hostname, Type.A);
                lookup.setResolver(resolver);
                Cache cache = new Cache();
                lookup.setCache(cache);
                // 开始解析，获取到解析后的记录
                lookup.run();
                // 确定解析 ok
                System.out.println("getDnsRealIp lookup.getResult(): " + (lookup.getResult() == Lookup.SUCCESSFUL));
                if (lookup.getResult() == Lookup.SUCCESSFUL) {
                    // 获取解析结果记录
                    Record[] records = lookup.getAnswers();
                    // log.debug("getDnsRealIp | dnsServerName: {} | record: {}", hostname, records);
                    System.out.println("getDnsRealIp hostname: " + hostname + ", records size: " + records.length);

                    // 将解析记录转为 InetAddress
                    InetAddress[] inetAddressArr = Arrays.stream(records).map(record -> {
                        if (record instanceof ARecord) {
                            return ((ARecord) record).getAddress();
                        }
                        return null;
                    }).filter(Objects::nonNull).toArray(InetAddress[]::new);
                    System.out.println("getDnsRealIp hostname: " + hostname + ", inetAddressArr size: " + inetAddressArr.length);
                    Arrays.asList(inetAddressArr).stream().forEach(adr -> System.out.println("getDnsRealIp hostname: " + hostname + ", the ip is: " + (adr==null? "null": adr.getHostAddress())));
                    // log.debug("getDnsRealIp | dnsServerName: {} | inetAddressArr: {}", hostname, inetAddressArr);
                    return inetAddressArr;
                }
            } catch (Exception e){
                e.printStackTrace();
                // log.error("getDnsRealIp error=====>", e);
            }
        }

        // 没解析或者 解析失败，走正常解析
        try {
            System.out.println("getDnsRealIp failed, use norm dns. hostname: " + hostname);
            InetAddress[] addresses = InetAddress.getAllByName(hostname);
            return addresses;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
        // throw new UnknownHostException("getDnsRealIp error | dnsServerName: " + hostname);
    }
    public InetAddress getOneAddress(String hostname) {
        System.out.println("getOneAddress called, hostname: " + hostname + ", resolver: " + resolver);
        if(null != resolver) {
            // 创建解析器，通过构造函数指定 DNS 服务器地址
            try {
                // 定义要解析的域名，以及解析类型（这里指解析 A 记录）
                Lookup lookup = new Lookup(hostname, Type.A);
                lookup.setResolver(resolver);
                Cache cache = new Cache();
                lookup.setCache(cache);
                // 开始解析，获取到解析后的记录
                lookup.run();
                // 确定解析 ok
                System.out.println("getOneAddress lookup.getResult(): " + (lookup.getResult() == Lookup.SUCCESSFUL));
                if (lookup.getResult() == Lookup.SUCCESSFUL) {
                    // 获取解析结果记录
                    Record[] records = lookup.getAnswers();

                    // log.debug("getDnsRealIp | dnsServerName: {} | record: {}", hostname, records);
                    System.out.println("getOneAddress hostname: " + hostname + ", records size: " + records.length);

                    // 将解析记录转为 InetAddress
                    InetAddress[] inetAddressArr = Arrays.stream(records).map(record -> {
                        if (record instanceof ARecord) {
                            return ((ARecord) record).getAddress();
                        }
                        return null;
                    }).filter(Objects::nonNull).toArray(InetAddress[]::new);
                    System.out.println("getOneAddress hostname: " + hostname + ", inetAddressArr size: " + inetAddressArr.length);
                    Arrays.asList(inetAddressArr).stream().forEach(adr -> System.out.println("getDnsRealIp hostname: " + hostname + ", the ip is: " + (adr==null? "null": adr.getHostAddress())));
                    // log.debug("getDnsRealIp | dnsServerName: {} | inetAddressArr: {}", hostname, inetAddressArr);
                    // random return one，the order of Array CoreDNS returned is Different every time as open loadbalance setting
                    InetAddress inetAddress = inetAddressArr[0];
                    if(inetAddressArr.length > 1) {
                        Random r = new Random();
                        int n = r.nextInt(inetAddressArr.length);
                        inetAddress = inetAddressArr[n];
                    }
                    return inetAddress;
                }
            } catch (Exception e){
                e.printStackTrace();
                // log.error("getDnsRealIp error=====>", e);
            }
        }

        // 没解析或者 解析失败，走正常解析
        try {
            System.out.println("getDnsRealIp failed, use norm dns. hostname: " + hostname);
            InetAddress address = InetAddress.getByName(hostname);
            return address;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
        // throw new UnknownHostException("getDnsRealIp error | dnsServerName: " + hostname);
    }

    public String domainToIpUrl(String url) {
        System.out.println("domainToIpUrl called, original url: " + url);
        String schema = "";
        if(url.startsWith("https://")) {// TODO，如果是 HTTPS，应该不能把域名换成IP来访问，待验证
            url = url.substring(8);
            schema = "https://";
            //return url;
        } else if(url.startsWith("http://")) {
            url = url.substring(7);
            schema = "http://";
        }
        String ipurl = url;
        int pos = url.indexOf("/");
        if(pos > 0) {
            String domain = url.substring(0, pos);
            String path = url.substring(pos);
            String host = domain;
            String port = "";
            int hpPos = domain.indexOf(":");
            if(hpPos > 0) {
                host = domain.substring(0, hpPos);
                port = domain.substring(hpPos);
            }
            try {
                // InetAddress ia = InetAddress.getByName(host); //
                InetAddress[] ias = getDnsRealIps(host);
                if(ias != null && ias.length > 0) {
                    // In fact, the order of Array CoreDNS returned is Different every time as open loadbalance setting
                    InetAddress ia = ias[0];
                    // if(ias.length > 1) { // CoreDNS 返回的列表已经轮询排序了，这里再加随机，可能反而会不均匀
                    //     Random r = new Random();
                    //     int n = r.nextInt(ias.length);
                    //     ia = ias[n];
                    // }
                    System.out.println("address: " + ia.getHostAddress());
                    ipurl = ia.getHostAddress() + port + path;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        String newUrl = schema+ipurl;
        System.out.println("domainToIpUrl after dns ipurl: " + (newUrl));
        return newUrl;
    }
}
